package com.ruoyi.plus.util;

import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.system.mapper.SysUserMapper;
import com.ruoyi.system.service.ISysMenuService;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
@EnableAsync
public class OnlineTokenService {

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private ISysMenuService menuService;

    private long sleepTime = 2000L;

    @Async
    public void forceLogoutByUserId(Long userId) {
        Collection<String> keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + userId + ":" + "*");
        if (!keys.isEmpty()) {
            for (String key : keys) {
                redisCache.deleteObject(key);
            }
        }
    }

    @Async
    public void forceLogoutByUserId(Long[] userIds) {
        if (userIds != null && userIds.length > 0L) {
            for (Long userId : userIds) {
                forceLogoutByUserId(userId);
            }
        }
    }

    @Async
    public void forceLogoutByUserId(List<Long> userIds) {
        if (userIds != null && userIds.size() > 0L) {
            for (Long userId : userIds) {
                forceLogoutByUserId(userId);
            }
        }
    }

    @Async
    public void forceLogoutBySysUser(List<SysUser> users) {
        if (users != null && users.size() > 0L) {
            for (SysUser user : users) {
                forceLogoutByUserId(user.getUserId());
            }
        }
    }

    @SneakyThrows
    @Async
    public void forceLogoutByRoleId(Long roleId) {
        Thread.sleep(sleepTime);
        List<SysUser> sysUsers = sysUserMapper.selectUserByRoleIds(Arrays.asList(roleId));
        this.forceLogoutBySysUser(sysUsers);
    }

    @SneakyThrows
    @Async
    public void forceLogoutByRoleId(List<Long> roleIds) {
        Thread.sleep(sleepTime);
        List<SysUser> sysUsers = sysUserMapper.selectUserByRoleIds(roleIds);
        this.forceLogoutBySysUser(sysUsers);
    }

    @Async
    public void updateLoginUserToken(List<SysUser> sysUsers) {
        if (sysUsers != null && sysUsers.size() > 0) {
            sysUsers.forEach(sysUser -> {
                updateLoginUserToken(sysUser);
            });
        }
    }

    @SneakyThrows
    @Async
    public void updateLoginUserToken(Long[] userIds) {
        Thread.sleep(sleepTime);
        if (userIds != null && userIds.length > 0L) {
            for (Long userId : userIds) {
                SysUser sysUser = sysUserMapper.selectUserById(userId);
                updateLoginUserToken(sysUser);
            }
        }
    }

    @SneakyThrows
    @Async
    public void updateLoginUserToken(Long userId) {
        Thread.sleep(sleepTime);
        if (userId != null) {
            SysUser sysUser = sysUserMapper.selectUserById(userId);
            updateLoginUserToken(sysUser);
        }
    }

    @SneakyThrows
    @Async
    public void updateLoginUserTokenByRole(List<SysRole> roles) {
        Thread.sleep(sleepTime);
        if (roles != null) {
            List<Long> roleIds = roles.stream().map(SysRole::getRoleId).collect(Collectors.toList());
            List<SysUser> sysUsers = sysUserMapper.selectUserByRoleIds(roleIds);
            this.updateLoginUserToken(sysUsers);
        }
    }

    @SneakyThrows
    @Async
    public void updateLoginUserTokenByRole(Long[] roleIds) {
        Thread.sleep(sleepTime);
        List<SysUser> sysUsers = sysUserMapper.selectUserByRoleIds(Arrays.asList(roleIds));
        this.updateLoginUserToken(sysUsers);
    }

    @SneakyThrows
    @Async
    public void updateLoginUserTokenByRole(Long roleId) {
        Thread.sleep(sleepTime);
        List<SysUser> sysUsers = sysUserMapper.selectUserByRoleIds(Arrays.asList(roleId));
        this.updateLoginUserToken(sysUsers);
    }

    @Async
    @SneakyThrows
    public void updateLoginUserToken(SysUser sysUser) {
        if (sysUser != null) {
            Collection<String> keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + sysUser.getUserId() + ":" + "*");
            if (!keys.isEmpty()) {
                Set<String> menuPermission = this.getMenuPermission(sysUser);
                for (String key : keys) {
                    LoginUser loginUser = redisCache.getCacheObject(key);
                    loginUser.setUser(sysUser);
                    loginUser.setDeptId(sysUser.getDeptId());
                    loginUser.setPermissions(menuPermission);
                    redisCache.setCacheObject(key, loginUser, loginUser.getExpireTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
                }
            }
        }

    }

    /**
     * 获取菜单数据权限
     *
     * @param user 用户信息
     * @return 菜单权限信息
     */
    public Set<String> getMenuPermission(SysUser user) {
        Set<String> perms = new HashSet<String>();
        // 管理员拥有所有权限
        if (user.isAdmin()) {
            perms.add("*:*:*");
        } else {
            List<SysRole> roles = user.getRoles();
            if (!CollectionUtils.isEmpty(roles)) {
                // 多角色设置permissions属性，以便数据权限匹配权限
                for (SysRole role : roles) {
                    Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());
                    role.setPermissions(rolePerms);
                    perms.addAll(rolePerms);
                }
            } else {
                perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId()));
            }
        }
        return perms;
    }

}
